package com.opdar.athena.client.agent.base;

import com.opdar.athena.client.agent.server.IoSession;
import com.opdar.platform.core.session.ISessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by shiju on 2017/7/2.
 */
public class TcpSessionManager implements ISessionManager<IoSession> {
    private ConcurrentHashMap<String, SessionTask> sessions = new ConcurrentHashMap<String, SessionTask>();
    private ConcurrentHashMap<String, SessionTask> agentSessions = new ConcurrentHashMap<String, SessionTask>();
    private Logger logger = LoggerFactory.getLogger("TcpSessionManager");
    private AtomicBoolean isDestory = new AtomicBoolean(false);

    public TcpSessionManager() {
    }

    public void set(String token, IoSession session, long timeout) {
        TcpSessionManager.SessionTask task = null;
        if(session.isAgent()){
            this.agentSessions.put(token, task = new TcpSessionManager.SessionTask(token, session, timeout));
        }else{
            this.sessions.put(token, task = new TcpSessionManager.SessionTask(token, session, timeout));
        }
        if(!session.isHttp()){
            session.executor().execute(task);
        }
    }

    public String set(IoSession session, long timeout) {
        String token = UUID.randomUUID().toString();
        this.set(token, session, timeout);
        this.logger.debug("用户「{}」登陆.", token);
        return token;
    }

    public IoSession get(String token) {
        return this.sessions.containsKey(token)?(this.sessions.get(token)).ioSession:
                agentSessions.containsKey(token)?agentSessions.get(token).ioSession:null;
    }

    public void clearTimeout(String token) {
        if(token != null){
            SessionTask session = this.sessions.get(token);
            if(session == null)session = this.agentSessions.get(token);
            session.clearTimeout();
        }
    }

    public void destory() {
        for(Map.Entry<String, TcpSessionManager.SessionTask> sessionTaskEntry:sessions.entrySet()){
            sessionTaskEntry.getValue().ioSession.downline();
            sessionTaskEntry.getValue().ioSession = null;
        }
        for(Map.Entry<String, TcpSessionManager.SessionTask> sessionTaskEntry:agentSessions.entrySet()){
            sessionTaskEntry.getValue().ioSession.downline();
            sessionTaskEntry.getValue().ioSession = null;
        }

    }

    public void remove(String token) {
        SessionTask task = this.sessions.remove(token);
        if(task ==null)task = agentSessions.remove(token);
        else{
            task.isRunning.set(false);
            task.ioSession = null;
        }
    }

    public void each(Eachback<String, IoSession> eachback,long num) {
        Iterator<Map.Entry<String, SessionTask>> it = sessions.entrySet().iterator();
        for(int i=0;i<num && it.hasNext();i++){
            final Map.Entry<String, SessionTask> entry = it.next();
            if(!entry.getValue().ioSession.isAgent())
            eachback.get(entry.getKey(),entry.getValue().ioSession);
        }
    }


    public void each(Eachback<String, IoSession> eachback) {
        for(Iterator<Map.Entry<String, SessionTask>> it = sessions.entrySet().iterator(); it.hasNext();){
            final Map.Entry<String, SessionTask> entry = it.next();
            if(!entry.getValue().ioSession.isAgent())
            eachback.get(entry.getKey(),entry.getValue().ioSession);
        }
    }

    private class SessionTask implements Runnable {
        private String token;
        private IoSession ioSession;
        private long timeout = Configuration.getConfiguration().getHeartbeatTime();
        private long heartbeatFrequency = Configuration.getConfiguration().getHeartbeatFrequency();
        private long overtimeFrequency = 0;
        private long lastRefreshTime = System.currentTimeMillis();
        private AtomicBoolean isRunning = new AtomicBoolean(true);


        SessionTask(String token, IoSession session,long timeout) {
            this.token = token;
            this.ioSession = session;
            this.timeout = timeout;
        }

        void clearTimeout() {
            overtimeFrequency = 0;
            this.lastRefreshTime = System.currentTimeMillis();
        }

        public void run() {
            if(this.isRunning.get() && !TcpSessionManager.this.isDestory.get()) {
                long t = System.currentTimeMillis() - this.lastRefreshTime;
                if(t >= this.timeout) {
                    overtimeFrequency++;
                    if(overtimeFrequency == heartbeatFrequency){
                        this.isRunning.set(false);
                        TcpSessionManager.this.logger.info("用户「{}」心跳超时", this.token);
                        ioSession.downline();
                    }else{
                        ioSession.heartbeat();
                        ioSession.executor().schedule(this,timeout, TimeUnit.MILLISECONDS);
                    }
                } else {
                    long nextScheduleTime = this.timeout - t;
                    ioSession.executor().schedule(this,nextScheduleTime, TimeUnit.MILLISECONDS);
                }
            }

        }
    }

    public int size(){
        return sessions.size();
    }
}
